webpack基本用法及原理(10000+)

您所在的位置:网站首页 webpack 怎么用 webpack基本用法及原理(10000+)

webpack基本用法及原理(10000+)

#webpack基本用法及原理(10000+)| 来源: 网络整理| 查看: 265

1 webpack是什么

所有工具的出现,都是为了解决特定的问题,那么前端熟悉的webpack是为了解决什么问题呢?

1.1 为什么会出现webpack

js模块化: 浏览器认识的语言是HTML,CSS,Javascript,而其中css和javascript是通过html的标签link,script引入进来。

随着前端项目的越来越复杂,css和js文件会越来越庞大,那么在开发阶段,就必须要把css和js按功能拆分成几个小文件,方便开发。

那么拆分的小文件如何引入到html中呢?css可以通过link标签或者@importcss语法,但是js因为没有模块导入的语法(ES6有了import,但还不是所有浏览器兼容),就只能通过script标签引入。但是这样的话会导致很多问题:

http请求大量增多,影响页面呈现速度。 全局变量混乱,难以维护。

针对js模块化出现了很多的解决方案,总结来说有几种规范:

CommonJs,语法为:require(), module.exports(同步加载,适用于node服务器环境) ES6 Mode,语法为:import,export(异步加载,适用于浏览器环境) AMD,语法为:require(),define()(异步加载,适用于浏览器环境)

工程化: 除了js模块化的问题之外,前端还有很多其他的问题,比如代码混淆,代码压缩,scss,less等css预编译语言的编译,typescript的编译,eslint检验代码规范,如果这些任务都需要手工去执行的话,太繁琐,也容易出错。

1.2 webpack能做什么

模块化 其实webpack的核心就是解决js模块化问题的工具,运行在node环境中,同时可以支持commonjs,es6,amd的模块语法(可以使用:reuire/mocule.exports,import/export,require/define的方式来导入导出模块)。

可以将开发时候拆分为不同文件的js代码,打包成一个js文件。也可以通过配置灵活的拆分js代码,通过 tree shaking 删减没有使用到的代码。

模块化打包时webpack的核心功能,但是它还有两个非常重要的机制loader和plugin。

loader webpack本身只支持js,json文件的模块化打包,但是有开放出loader接口,通过不同的loader可以将其他格式的文件转化为可识别的模块,比如: css-loader可以识别css文件,raw-loader可以直接将文件当作模块,less-loader,sass-loader可以直接识别less,sass文件。

plugin 插件机制是webpack的另一个重要的拓展,webpack在打包的过程中,会暴露出不同的生命周期事件,而插件会监听这些事件,然后做出对应的操作,比如: UglifyJsPlugin可以混淆压缩代码,EslintWebpackPlugin可以执行eslint的代码格式检测和自动修复。

总结: webpack是一个运行在node环境下,对js文件进行模块化打包的工具。通过loader机制可以实现除js格式外的其他格式文件,通过plugin机制可以实现自动执行一些工程化需要的任务。

2 webpack怎么用

那么webpack要怎么使用呢?

2.1 安装运行

首先要安装webapck,使用npm(npm基本用法及原理),安装webpack(核心),webpack-cli(命令行工具):

npm install webpack webpack-cli

然后创建以下两个文件:name.js,index.js, 我们以es6的语法导入导出模块,es6模式的模块变量的导出时按引用导出,就是在模块的变量如果在外部被修改,也会作用到模块内部,而commonjs的模式是按值导出,即模块外部的修改,不会影响到模块内部。

//name.js let name = "小明" function say(){ console.log('my name is ',name) } export { name, say } //index.js import { name,say } from "./name.js"; name = "小红" say() console.log('he name is ',name)

然后运行打包命令:

//用npx直接运行webpack命令 npx webpack //或者用npm的脚本运行打包 //package.json { script:{ pack:'webpack' } } npm run pack

webpack默认从index.js文件开始打包,所以如果开始文件的名称为index,就可以不需要写配置文件,就可以直接打包。 默认打包的模式是‘production'即生产模式,打包成功后,会自动创建dist文件夹,并生成main.js文件:

//main.js (()=>{"use strict";let e="小明";e="小红",console.log("my name is ","小红"),console.log("he name is ","小红")})();

我们看到打包后的文件把index.js和name.js两个文件合成了一个文件,并对代码进行了混淆压缩(生产模式),这是最基本的webpack最核心的功能 6— 打包。 但是显然,在实际工作中我们不会这么简单的使用,那就需要用的配置文件了,下面是一个比较接近实际工作中的例子。

2.2 配置文件 webpack-config.js

以下的项目会有几个文件:index.js , utils.js, style.scss, index.html, webpack-config.js。 基本功能就是在index.js文件中引入utils.js文件里的方法并调用。然后用scss语法编写样式,最后把打包的文件加入到已有的index.html文件中。 通过命令:npx webpack serve(可以放入npm脚本配置中,然后运行 npm run xxx),实现的效果是:

scss自动编译 index.js utils.js style.scss 文件打包成一个文件 把打包的文件自动添加到index.html中 打包完成后,自动打开默认浏览器,查看页面 文件有变更的话,会自动重新打包,刷新页面 //utils.js export function sayHello(){ console.log('hello world') } //index.js import './styles.scss' import { sayHello } from "./utils"; sayHello() /*styles.scss*/ $bg : black; $fontC:rgb(218, 17, 117); body{ background:$bg; h3{ color:$fontC; } } webpack hello webpack //webpack-config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 打包模式 mode:'development', //development,production,none devtool:'cheap-source-map', // eval-source-map,source-map,cheap-source-map // 入口配置 entry: { app: './src/index.js', }, // 出口配置 output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), clean: true,//每次打包,清除dist文件夹 }, // 本地服务器 devServer:{ port:8888,//端口 open:true,//自动打开浏览器 hot:true,//启动热更新 }, // loader module:{ // 处理scss文件 rules:[ { test:/\.s[ac]ss$/i, use:[ 'style-loader',//将js模块生成style标签节点 'css-loader',//将css转化成js模块 'sass-loader'//将scss文件编译成css文件 ] } ] }, // 插件 plugins: [ // 自动把打包后的文件加入到html文件 new HtmlWebpackPlugin({ // 生成html文件的模板 template: './index.html' }), ], };

webpack的打包是在node环境下执行的,所以node的语法这里都可以用,最终输出的是一个js对象。 配置可以分成几个部分(参考webpack配置文档):

打包模式

mode 预置了开发环境和生产环境的一些优化。 在这里插入图片描述

devtool 控制是否生成,以及如何生成 source map。有了source map文件的话,如果代码有报错可以映射到打包之前的代码(源代码),可以方便定位错误。 可以有很多的选择,一般来说,在开发环境下选择:eval-cheap-module-source-map,cheap-source-map。生产环境选择:不配置,source-map。

入口、出口

entry 入口文件,webpack会从这个文件开始查找依赖的包,可以配置多个 output 出口文件,webpack会根据这里的配置,输出打包后的文件。

本地服务器 此功能需要安装webpack-dev-server插件npm install --save-dev webpack-dev-server,启动时需要用serve命令npx webpack serve. 启动成功后,会在本地开启一个web服务器,并且有实时更新,热模块替换等功能。 配置项是在devServer中。

loader webpack本是只支持对js文件的打包,但是因为有loader机制,可以通过配置rules实现对其他文件的打包。 示例代码中实现的是对scss/sass文件的打包,同一个relues中的loader的执行顺序是从右到左(逆序),所以顺序不能乱。第一个执行的loader 会将其结果(被转换后的资源)传递给下一个 要执行的loader。

插件plugins webpack在打包的时候,会暴露其生命周期,插件就是在特定的生命周期执行的操作,通过插件的机制,可以实现很多强大的功能。 示例代码中使用了HtmlWebpackPlugin插件,功能是在打包完成后,自动把打包后的代码加入到html文件中。如果没有任何配置,则会自动生成一个html文件,并通过标签把js文件引入进来。

3 实践中的优化 3.1 配置文件拆分与合并--merge

在实际项目中,开发环境和生产环境的配置往往会有很大的区别,所以会有两个配置文件,而这两个配置文件又会有一些公共的配置,所以就会有如下三个配置文件:

webpack.dev.js webpack.prod.js webpack.common.js 那么这些配置文件是如何结合的呢?这就要用到webpack-merge插件了。 // webpack.common.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 入口配置 entry: { app: './src/index.js', }, // 出口配置 output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), clean: true,//每次打包,清除dist文件夹 }, // 插件 plugins: [ // 自动把打包后的文件加入到html文件 new HtmlWebpackPlugin({ // 生成html文件的模板 template: './index.html' }), ], }; //webpack.dev.js const { merge } = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'development', devtool: 'eval-source-map', // 本地服务器 devServer:{ port:8888,//端口 open:true,//自动打开浏览器 hot:true,//启动热更新 }, }); //webpack.prod.js const { merge } = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'production', devtool:'source-map' }); // package.json { ... "scripts": { "dev": "webpack serve --config webpack.dev.js", "build": "webpack --config webpack.prod.js", }, ... }

然后分别执行npm run dev,npm run build就可以了。

3.2 代码分离

webpack会把所有代码打包成一个文件(包括业务代码,npm包),这样最后的包就会很大,打包效率也很慢,所以可以有时候需要做代码分离。

第三方库分离 有一些第三方库可能会需要独立引入,而不是放在业务代码里面,因为不会改动或者需要cdn服务,比如jquery有免费的cdn服务:https://upcdn.b0.upaiyun.com/libs/jquery/jquery-2.0.2.min.js 。 那这些独立引入的js文件就不需要加入到webpack打包,只需要在externals添加配置就行。

// webpack.config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 打包模式 mode:'development', //development,production,none devtool:'cheap-source-map', // eval-source-map,source-map,cheap-source-map // 入口配置 entry: { app: './src/index.js', }, // 出口配置 output: { filename: '[name].[contenthash].js',//contenthash 是文件内容的hash值 path: path.resolve(__dirname, 'dist'), clean: true,//每次打包,清除dist文件夹 }, // 插件 plugins: [ // 自动把打包后的文件加入到html文件 new HtmlWebpackPlugin({ // 生成html文件的模板 template: './index.html' }), ], };

这样的话,虽然index.js里有引入jquery,webpack也不会把jquery打包进来,打包时间会减少,包的体积也会变小。

npm包分离 第三方库除了一些可以用script标签引入的,大多数是通过npm引入的,这一类的js包也会也会合并到最后的app.js总包之中,使得app.js文件会过大,而且如果业务代码有一点改动的话,app.js的包就会全部都变动,导致浏览器就会重新下载app.js文件,使用不了浏览器内置的缓存机制。 我们可以通过配置,让npm里的包与业务代码分开。

// index.js import _ from 'lodash' import $ from 'jquery' import { sayHello } from "./utils" $('#title').text('hello jquery') // webpack.config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 打包模式 mode:'development', //development,production,none devtool:'cheap-source-map', // eval-source-map,source-map,cheap-source-map // 入口配置 entry: { app: './src/index.js', }, // 出口配置 output: { filename: '[name].[contenthash].js', //contenthash是文件内容的hash值 path: path.resolve(__dirname, 'dist'), clean: true,//每次打包,清除dist文件夹 }, // 插件 plugins: [ // 自动把打包后的文件加入到html文件 new HtmlWebpackPlugin({ // 生成html文件的模板 template: './index.html' }), ], optimization: { runtimeChunk: 'single',// 把webpack引导文件独立出来 splitChunks: { cacheGroups: { vendor: { //所有node_modules下的包合并成一个,并独立出来 test: /[\\/]node_modules[\\/]/, //控制哪种导入方式的js包才分离出来, 'all'-全部的js包,'async'-异步导入的js包,'initial'-初始导入的js包 chunks: 'all', name:'vendor' //独立后的包的名称 } } } }, };

这样打包目录下就有三个文件:

app.js: 业务代码 runtime.js:webpack的引导代码 vendor.js: npm引入的js包代码

一般有变动的就只有app.js文件了。npm引入的js包是否可以再分成几个文件呢?可以的,参看 SplitChunksPlugin文档

业务代码分离 有时候不仅第三方库需要分离,我们自己写的业务代码可能也会很大,也需要分离。要实现业务代码的分离只要添加多个入口就可以了。

// index.js import { sayHello } from "./utils" sayHello() console.log('hello index') // utils.js export function sayHello(){ console.log('hello utils') } // webpack.config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'development', devtool: 'cheap-source-map', // 入口配置 entry: { app: { import:'./src/index.js', dependOn:'utils' }, utils:'./src/utils.js' }, // 出口配置 output: { filename: '[name].[contenthash].js', path: path.resolve(__dirname, 'dist'), clean: true,//每次打包,清楚dist文件夹 }, // 插件 plugins: [ // 自动把打包后的文件加入到html文件 new HtmlWebpackPlugin({ // 生成html文件的模板 template: './index.html' }), ], };

这样utils.js文件也从主包app.js中分离了出来,要注意的是app入口加了dependOn:'utils',为了让app.js里面不要重复打包utils.js。

动态加载 有时候不是需要页面一开始的时候,就加载全部的js包,而是等到特定的时机再去加载某些js包,这就需要动态加载了,只需要用到import()就可以了,注意这是import的函数使用方式。

// index.js const element = document.createElement('div'); element.id = 'title' element.innerHTML ='Hello webpack'; const button = document.createElement('button'); button.innerHTML = 'Click me'; button.onclick = importJquery; document.body.appendChild( element); document.body.appendChild( button); async function importJquery(){ const { default: $ } = await import('jquery'); $('#title').text('hello jquery') } // webpack.config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'development', devtool: 'cheap-source-map', // 入口配置 entry: { app: { import:'./src/index.js', }, }, // 出口配置 output: { filename: '[name].[contenthash].js', path: path.resolve(__dirname, 'dist'), clean: true,//每次打包,清楚dist文件夹 }, // 插件 plugins: [ // 自动把打包后的文件加入到html文件 new HtmlWebpackPlugin({ // 生成html文件的模板 template: './index.html' }), ], };

如果运行代码的话,会发现页面一开始进入的时候,并没有引入jquery的包,但是点击按钮的时候,就开始导入了,这就动态加载。 并且发现webpack.config.js并没有做什么特殊的配置,这是因为动态导入的js包webpack会自动给独立为一个js文件。 import()返回的是一个promise对象。

3.3 动态链接库 dll

webpack每次打包的时候,都会把涉及到的js包都处理一遍。但是实际上有些js包是不会有改动到的,所以打包过后的文件每次都是一样的,每次都重新打包的话,会加增打包时间。 有一种解决方案是:把不会变动的js包先打包一次,以后每次打包的时候,直接引用就可以了。 先添加一个独立的配置文件 webpack.dll.config.js

//webpack.dll.config.js const path = require('path'); const webpack = require('webpack'); module.exports = { mode: 'production', // 入口文件 entry: { // 项目中用到该两个依赖库文件 jquery_lodash: ['jquery','lodash'], }, // 输出文件 output: { // 文件名称 filename: '[name].dll.js', // 将输出的文件放到dll目录下 path: path.resolve(__dirname, 'dll'), // 文件输出的全局变量 library: '_dll_[name]', }, plugins: [ // 使用插件 DllPlugin new webpack.DllPlugin({ // 动态链接库的全局变量名称,需要和 output.library 中保持一致 // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值 name: '_dll_[name]', // 描述动态链接库的 manifest.json 文件输出时的文件名称 path: path.join(__dirname, 'dll', '[name].manifest.json') }), ] };

执行打包脚本 npx webpack --config webpack.dll.config.js,就会再dll目录下输出文件:jquery_lodash.dll.js,jquery_lodash.manifest.json。 主要用到的插件是:

DllPlugin 的作用就是生成manifest.json文件。

然后配置项目打包用的webpack.config.js文件:

//webpack.config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); module.exports = { mode: 'development', devtool: 'cheap-source-map', // 入口配置 entry: { app: { import:'./src/index.js', }, }, // 出口配置 output: { filename: '[name].[contenthash].js', path: path.resolve(__dirname, 'dist'), clean: true,//每次打包,清楚dist文件夹 }, // 插件 plugins: [ // 自动把打包后的文件加入到html文件 new HtmlWebpackPlugin({ // 生成html文件的模板 template: './index.html' }), // 引用dll中的文件, new webpack.DllReferencePlugin({ context: __dirname, manifest: require('./dll/jquery_lodash_dll.manifest.json') }), //把dll文件加入到index.html中 new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, './dll/jquery_lodash_dll.dll.js'), publicPath: './', }), ], };

主要用到的插件是:

DllReferencePlugin 检索引用文件的时候,如果发现manifest.json里面有,就告诉webpack不要打包该文件,因为已经打包好了。 AddAssetHtmlWebpackPlugin 因为webpack没有打包dll里的文件,所以需要手动把它加入到index.html中。

然后就可以正常的打包项目了:

// index.js import _ from 'lodash' import $ from 'jquery' $('#title').text('hello jquery')

执行打包脚本 npx webpack,会自动使用webpack.config.js配置文件打包。会发现打包速度提高了很多。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3